﻿#include "module_XExpand_Network.h"

HANDLE w_DefaultHeap;

X_WinHttp::X_WinHttp() {
    if (!w_DefaultHeap) {
        w_DefaultHeap = GetProcessHeap();
    }
    __Create();
    __InitOptions();
}

X_WinHttp::X_WinHttp(CXText url) {
    if (!w_DefaultHeap) {
        w_DefaultHeap = GetProcessHeap();
    }
    __Create();
    __InitOptions();
    Open(url);
}

X_WinHttp::X_WinHttp(CXText url, CXText Method) {
    if (!w_DefaultHeap) {
        w_DefaultHeap = GetProcessHeap();
    }
    __Create();
    __InitOptions();
    Open(url, Method);
}

X_WinHttp::~X_WinHttp() { __Close(); }

void X_WinHttp::__Create() {
    //初始化Winhttp
    hSession = WinHttpOpen(L"Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2)",
        WINHTTP_ACCESS_TYPE_NO_PROXY, WINHTTP_NO_PROXY_NAME,
        WINHTTP_NO_PROXY_BYPASS, NULL);
    //申请缓冲区
    Dataptr = HeapAlloc(w_DefaultHeap, NULL, HTTP_BUFFER_LEN);
    NeverRedirect = true;
}

void X_WinHttp::__Create(CXText User_Agent) {
    //初始化Winhttp
    hSession = WinHttpOpen(User_Agent.get(), WINHTTP_ACCESS_TYPE_NO_PROXY,
        WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, NULL);
    //申请缓冲区
    Dataptr = HeapAlloc(w_DefaultHeap, NULL, HTTP_BUFFER_LEN);
}

void X_WinHttp::__Close() {
    //关闭WinHttp各句柄
    if (!hRequest) {
        WinHttpCloseHandle(hRequest);
    }
    if (!hConnect) {
        WinHttpCloseHandle(hConnect);
    }
    if (!hSession) {
        WinHttpCloseHandle(hSession);
    }

    //释放缓冲区
    if (!Dataptr) {
        HeapFree(w_DefaultHeap, NULL, Dataptr);
    }
    //初始化各状态
    Dataptr = NULL;
    hRequest = hConnect = hSession = NULL;
    ResponseBodylen = ContentLength = Port = NULL;
    NeverRedirect = SetProxyInfo = false;
    ResponseHeaders = L"";
    SetProxyUser = L"";
    SetProxyPass = L"";
    HostName = L"";
    UrlW = L"";
}

void X_WinHttp::__InitOptions() {
    _Auto = true;
    _AutoCookies = 0;
    _IsIgnoreError = true;  // 自动忽略错误
    _Cookies = L"; ";
    _Cookieslen = 2;
    // 设置默认超时 能有效避免Send失败
    WinHttpSetTimeouts(hSession, 10000, 15000, 120000, 300000);
}

void X_WinHttp::__UpCookies1(bool NopNullCookieName) {
    UINT i = ResponseHeaders.find(L"Set-Cookie:");
    UINT ii = 0;
    bool vbool;
    while (i != std::wstring::npos && ii != std::wstring::npos) {
        i += 11;
        ii = ResponseHeaders.find(L";");
        if (ii != std::wstring::npos) {
            CXText cookie = ResponseHeaders.substr(i, ii - i);
            lTrim(cookie);
            UINT iii = cookie.find(L"=");
            CXText Value;
            if (iii != std::wstring::npos) {
                // cookies计算
                CXText name = cookie.substr(0, iii);
                UINT OI = _Cookies.find(L"; " + name);  // Cookie名区分大小写emmm
                if (OI != std::wstring::npos) {
                    UINT OII = cookie.size();
                    if (NopNullCookieName && OII - 1 == iii) {
                        i = ResponseHeaders.find2(L"Set-Cookie:", ii);
                        continue;
                    }
                    OI += 2;
                    OII = _Cookies.find2(L"; ", name.size());
                    if (OII != std::wstring::npos) {
                        Value = cookie.substr(cookie.size() - 9, cookie.size());
                        if (Value == L"=deleted;" || Value == L"=DELETED;" ||
                            Value == L"==Deleted;") {  //判断是否是删除标记
                            if (NopNullCookieName) {
                                i = ResponseHeaders.find2(L"Set-Cookie:", ii);
                                continue;
                            }
                            cookie = L"";
                            OI -= 1;
                        }
                        _Cookies.replace(OI, OII - OI + 1, cookie);
                        vbool = true;
                    }
                }
                else {
                    Value = cookie.substr(cookie.size() - 9, cookie.size());
                    if (Value == L"=deleted;" || Value == L"=DELETED;" ||
                        Value == L"==Deleted;") {  //判断是否是删除标记
                        _Cookies = _Cookies + cookie + L" ";
                        vbool = true;
                    }
                }
            }
        }
        i = ResponseHeaders.find2(L"Set-Cookie:", ii);
    }
    if (vbool) {
        _Cookieslen = _Cookies.size();
    }
}

void X_WinHttp::__UpCookies2(bool NopNullCookieName) {
    UINT i = ResponseHeaders.find(L"Set-Cookie:");
    UINT ii = 0;
    while (i == std::wstring::npos && ii == std::wstring::npos) {
        i += 11;
        ii = ResponseHeaders.find2(L";", i);
        if (ii != std::wstring::npos) {
            ii++;
            CXText cookie = ResponseHeaders.substr(i, ii - i);
            lTrim(cookie);
            UINT iii = cookie.find(L"=");
            if (iii != std::wstring::npos) {
                CXText name = cookie.substr(0, iii - 1);
                CXText Value = cookie.substr(cookie.size() - 9, cookie.size());
                bool del = Value == L"=deleted;" || Value == L"=DELETED;" ||
                    Value == L"=Deleted;";
                if (del) {
                    if (NopNullCookieName) {
                        i = ResponseHeaders.find2(L"Set-Cookie:", ii);
                        continue;
                    }
                    Value = L"";
                }
                else {
                    iii += 1;
                    Value = cookie.substr(iii, cookie.size() - iii);
                    CXText tx = Value;
                    lTrim(tx);
                    if (NopNullCookieName && tx == L"") {
                        i = ResponseHeaders.find2(L"Set-Cookie:", ii);
                        continue;
                    }
                }
                InternetSetCookieW(UrlW, name, Value);
            }
            i = ResponseHeaders.find2(L"Set-Cookie:", ii);
        }
    }
}

bool X_WinHttp::__SetCookies2(CXText Cookie) {
    if (Cookie == L"") {
        return false;
    }
    bool vbool;
    std::vector<CXText> strs = split(Cookie, L';');
    for (size_t i = 0; i < strs.size(); i++) {
        lTrim(strs[i]);
        UINT ii = strs[i].find(L"=");
        if (ii != std::wstring::npos) {
            CXText name = strs[i].substr(0, ii - 1);
            CXText value = strs[i].substr(strs[i].size() - (strs[i].size() - ii),
                strs[i].size());
            InternetSetCookieW(UrlW, name, value);
            vbool = true;
        }
    }
    return vbool;
}

bool X_WinHttp::__LoadCookies1() {
    CXText HeaderW = L"Cookie:" + _Cookies.substr(3, _Cookieslen - 4);
    return WinHttpAddRequestHeaders(
        hRequest, HeaderW.get(), HeaderW.size(),
        WINHTTP_ADDREQ_FLAG_ADD | WINHTTP_ADDREQ_FLAG_REPLACE) > 0;
}

bool X_WinHttp::__LoadCookies2() {
    DWORD len = 0;
    wchar_t* cookies;
    InternetGetCookieW(UrlW, L"", NULL, &len);
    if (!len) {
        return false;
    }
    cookies = new wchar_t[len];
    InternetGetCookieW(UrlW, L"", cookies, &len);
    CXText HeaderW = L"Cookie:";
    HeaderW += cookies;
    delete cookies;
    return WinHttpAddRequestHeaders(
        hRequest, HeaderW, HeaderW.size(),
        WINHTTP_ADDREQ_FLAG_ADD | WINHTTP_ADDREQ_FLAG_REPLACE) > 0;
}

void X_WinHttp::__SetRequestHeaders(bool IsData) {
    WinHttpAddRequestHeaders(hRequest, L"Accept:*/*", 10,
        WINHTTP_ADDREQ_FLAG_ADD_IF_NEW);
    WinHttpAddRequestHeaders(hRequest, L"Accept-Language:zh-cn", 21,
        WINHTTP_ADDREQ_FLAG_ADD_IF_NEW);
    WinHttpAddRequestHeaders(hRequest, L"Cache-Control:no-cache", 22,
        WINHTTP_ADDREQ_FLAG_ADD_IF_NEW);
    if (IsPOST || IsData) {
        WinHttpAddRequestHeaders(hRequest,
            L"Content-Type:application/x-www-form-urlencoded",
            46, WINHTTP_ADDREQ_FLAG_ADD_IF_NEW);
    }
}

void X_WinHttp::__SetInternetInfo() {
    DWORD i = 0;
    if (SetProxyInfo) {
        // WinHttp的设置代理信息 必须使用请求句柄来设置 而且设置后是一直有效的
        // 真搞不懂
        //  为啥不能直接用会话句柄去设置呢？WinHttp的设置代理信息
        //  必须使用请求句柄来设置 而且设置后是一直有效的 真搞不懂
        //  为啥不能直接用会话句柄去设置呢？ 为什么不用 WinHttpSetCredentials
        //  呢？ 因为 WinHttpSetCredentials 没有 WinHttpSetOption 快 已测试
        WinHttpSetOption(hRequest, WINHTTP_OPTION_PROXY_USERNAME,
            (void*)SetProxyUser.get(), SetProxyUser.size());
        WinHttpSetOption(hRequest, WINHTTP_OPTION_PROXY_PASSWORD,
            (void*)SetProxyPass.get(), SetProxyPass.size());
        SetProxyInfo = false;
        SetProxyUser = L"";
        SetProxyPass = L"";
    }
    if (NeverRedirect ||
        _AutoCookies >
        NULL) {  //禁止重定向 这个设置后是本次有效的 所以必须每次都设置一次
        // WinHttpSetOption (hRequest, #WINHTTP_OPTION_REDIRECT_POLICY,
        // #WINHTTP_OPTION_REDIRECT_POLICY_NEVER, 4) ’这俩都是禁止重定向
        // 好像用哪个都一样
        i = WINHTTP_DISABLE_REDIRECTS;
        WinHttpSetOption(hRequest, WINHTTP_OPTION_DISABLE_FEATURE, &i, DwSize);
    }

    if (_IsIgnoreError) {  //设置一次自动忽略错误/证书错误 设置一次就一直有效
        //而且暂时没找到如何取消的标志
        i = 13056;
        WinHttpSetOption(hRequest, WINHTTP_OPTION_SECURITY_FLAGS, &i, DwSize);
        _IsIgnoreError = false;
    }
    if (_AutoCookies != NULL) {
        i = WINHTTP_DISABLE_COOKIES;
        WinHttpSetOption(hRequest, WINHTTP_OPTION_DISABLE_FEATURE, &i,
            DwSize);  //设置禁止Winhttp内部自动处理Cookies
        if (_AutoCookies == 1) {
            __LoadCookies1();
        }
        if (_AutoCookies == 2) {
            __LoadCookies2();
        }
    }
}

bool X_WinHttp::__GetQueryHeadersInfo(bool NopNullCookieName) {
    DWORD len = 0;
    WinHttpQueryHeaders(hRequest, WINHTTP_QUERY_RAW_HEADERS_CRLF, NULL, NULL,
        &len,
        NULL);  //先获取长度
    if (len < 1) {
        ContentLength = -1;
        return false;
    }
    wchar_t* Bin = new wchar_t[len];
    if (!WinHttpQueryHeaders(hRequest, WINHTTP_QUERY_RAW_HEADERS_CRLF, NULL, Bin,
        &len, NULL)) {
        ContentLength = -1;
        delete Bin;
        return false;
    }
    ResponseHeaders = Bin;
    delete Bin;
    //为了更好且更快的读取数据 这里提前获取了Content-Length信息来得到要读取的长度
    DWORD i = ResponseHeaders.find(L"Content-Length:");
    if (i == std::wstring::npos) {
        ContentLength = -1;  // 如果获取失败
    }
    else {
        i += 15;  // 加上 Content-Length:  的长度
        DWORD len = ResponseHeaders.find2(L"\n", i);
        if (len != std::wstring::npos) {
            ContentLength = _wtoi(ResponseHeaders.substr(i, len - i));
        }
        else {
            len = ResponseHeaders.size();  //万一没换行符
            ContentLength = _wtoi(ResponseHeaders.substr(ResponseHeaders.size() - 5,
                ResponseHeaders.size()));
        }
    }
    //处理并更新Cookies
    if (_AutoCookies) {
        switch (_AutoCookies) {
        case 1:
            __UpCookies1(NopNullCookieName);
            break;
        case 2:
            __UpCookies2(NopNullCookieName);
            break;
        }
    }
    return false;
}

void X_WinHttp::__Redirect(bool NopNullCookieName, int SendRetryCount) {
    if (!_AutoCookies || NeverRedirect) {  //模式0或者禁止重定向
        return;
    }
    CXText url = GetResponseLocation();
    if (url == L"") {
        _RedirectIndex = NULL;
        return;
    }
    _RedirectIndex++;
    if (_RedirectIndex > 20) {  //单次访问 最多20次重定向跳转
        _RedirectIndex = NULL;
        return;
    }
    Open(url);
    Send("", NopNullCookieName, SendRetryCount);
}

// === 公开集

void X_WinHttp::Auto(bool IsAuto) { _Auto = IsAuto; }

void X_WinHttp::AutoCookies(int Index) {
    _AutoCookies = Index > 2 || Index < 0 ? 0 : Index;
}

void X_WinHttp::Rest(CXText User_Agent) {
    __Close();
    if (User_Agent == L"") {
        __Create();
    }
    else {
        __Create(User_Agent);
    }
    __InitOptions();
}

bool X_WinHttp::Open(CXText Url, CXText Method) {  //打开一个HTTP连接
    if (hSession == NULL) {
        return false;
    }
    // 处理CrackUrl
    // WinHttpCrackUrl无法处理带有协议头的Url,需要自行处理
    CXText str;
    UrlW = Url;
    if (UrlW.size() < 8) {
        UrlW = L"http://" + UrlW;
    }
    else {
        str = UrlW.substr(0, 8);
        if ((_wcsicmp(str, L"https://") != 0) && (_wcsicmp(str.substr(0, 7), L"http://") != 0)) {
            UrlW = L"http://" + UrlW;
        }
    }
    URL_COMPONENTS urlComp;
    // Initialize the URL_COMPONENTS structure.
    ZeroMemory(&urlComp, sizeof(urlComp));
    urlComp.dwStructSize = sizeof(urlComp);
    // Set required component lengths to non-zero
    // so that they are cracked.
    urlComp.dwSchemeLength = (DWORD)-1;
    urlComp.dwHostNameLength = (DWORD)-1;
    urlComp.dwUrlPathLength = (DWORD)-1;
    urlComp.dwExtraInfoLength = (DWORD)-1;

    LPCWSTR pwszUrl = UrlW.get();
    if (!WinHttpCrackUrl(pwszUrl, (DWORD)wcslen(pwszUrl), 0, &urlComp)) {
        return false;
    }
    //初始化状态
    ResponseBodylen = ContentLength = NULL;
    ResponseHeaders = L"";
    //处理Connect
    if (hRequest) {
        WinHttpCloseHandle(hRequest);
    }
    CXText HostNameW;
    HostNameW = urlComp.lpszHostName;
    HostNameW = HostNameW.substr(0, HostNameW.find(L"/"));
    if (!hConnect || HostName != HostNameW ||
        urlComp.nPort != Port) {  //如果还是上次的域名与端口就直接复用Connect
        if (hConnect) {
            WinHttpCloseHandle(hConnect);
        }
		 UINT isp= HostNameW.find(L":");
    if (isp !=-1) {
        HostNameW = HostNameW.substr(0, HostNameW.find(L":"));
    }
        hConnect = WinHttpConnect(hSession, HostNameW, urlComp.nPort, NULL);
        if (!hConnect) {
            hRequest = NULL;
            return false;
        }
        HostName = HostNameW;
        Port = urlComp.nPort;
    }
    //处理 Request
    CXText MethodW;
    if (Method == L"") {
        MethodW = L"GET";
    }
    else {
        str = Method;
        w_toupper(str);
        if (_Auto) {
            IsPOST = str == L"POST";
            MethodW = str;
        }
        else {
            MethodW = str;
        }
    }
	
    CXText UrlPathW;
    if (urlComp.dwUrlPathLength < 1) {  //访问路径如果为空 自动使用斜杠
        UrlPathW = L"/";
    }
    else {
        UrlPathW = urlComp.lpszUrlPath;
    }
    DWORD dwFlags = 0;
    if (urlComp.nScheme == INTERNET_SCHEME_HTTPS) {  //判断是否为HTTPS
        dwFlags = WINHTTP_FLAG_SECURE;
    }
    //部署状态
    Scheme = urlComp.nScheme;  //保存协议 解析302
    hRequest = WinHttpOpenRequest(hConnect, MethodW, UrlPathW,
        WINHTTP_DEFAULT_VERSION, WINHTTP_NO_REFERER,
        WINHTTP_DEFAULT_ACCEPT_TYPES, dwFlags);
    return hRequest != NULL;
}

bool X_WinHttp::Send(CXTextA Data, bool NopNullCookieName, int SendRetryCount) {
    if (!hRequest) {
        return false;
    }
    //附加选项和加载Cookies
    __SetInternetInfo();
    //提交数据

    DWORD len = Data.size()>0?Data.size()-1:0;
    if (_Auto) {
        __SetRequestHeaders(len > NULL);
    }
    bool vbool;
    for (int i = 0; i < SendRetryCount + 1;
        i++) {  // Send和Recv有几率出现失败 可从新执行来解决 （放心 已经测试过
        // 从新执行 POST数据不会叠加 ）
        DWORD error = 0;
        vbool = WinHttpSendRequest(hRequest, WINHTTP_NO_ADDITIONAL_HEADERS, NULL,
            (void*)Data.get(), len, len, NULL) > 0;
        if (!vbool) {
            error = GetLastError();
            if (error != ERROR_WINHTTP_CANNOT_CONNECT &&
                error != ERROR_WINHTTP_CONNECTION_ERROR) {
                return false;
            }
            continue;
        }
        vbool = WinHttpReceiveResponse(hRequest, NULL) > 0;
        if (vbool) {
            break;
        }
        error = GetLastError();
        if (error != ERROR_WINHTTP_CANNOT_CONNECT &&
            error != ERROR_WINHTTP_CONNECTION_ERROR) {
            return false;
        }
    }
    if (!vbool) {
        return false;
    }
    __GetQueryHeadersInfo(NopNullCookieName);

    //处理重定向
    __Redirect(NopNullCookieName, SendRetryCount);
    return true;
}

bool X_WinHttp::SendBin(CXBytes Data, bool NopNullCookieName,
    int SendRetryCount) {
    if (!hRequest) {
        return false;
    }
    //附加选项和加载Cookies
    __SetInternetInfo();
    //提交数据
    if (_Auto) {  //处理Auto自动补全协议头
        __SetRequestHeaders(Data.size() == NULL);
    }
    bool vbool;
    for (int i = 0; i < SendRetryCount + 1; i++) {
        vbool = WinHttpSendRequest(hRequest, WINHTTP_NO_ADDITIONAL_HEADERS, NULL,
            (LPVOID)Data.get(), Data.size(), Data.size(),
            NULL) > 0;
        int error = 0;
        if (!vbool) {
            error = GetLastError();
            if (error != ERROR_WINHTTP_CANNOT_CONNECT &&
                error != ERROR_WINHTTP_CONNECTION_ERROR) {
                return false;
            }
            continue;
        }
        vbool = WinHttpReceiveResponse(hRequest, NULL) > 0;
        if (vbool) {
            break;
        }
        error = GetLastError();
        if (error != ERROR_WINHTTP_CANNOT_CONNECT &&
            error != ERROR_WINHTTP_CONNECTION_ERROR) {
            return false;
        }
    }
    if (!vbool) {
        return false;
    }

    //处理协议头和Cookies
    __GetQueryHeadersInfo(NopNullCookieName);

    //处理重定向
    __Redirect(NopNullCookieName, SendRetryCount);
    return true;
}

DWORD X_WinHttp::GetResponseBody(CXBytes& req) {
    if (hRequest == 0) {
        return {};
    }
    DWORD Ptri;
    if (ResponseBodylen != NULL) {
        req = "";
        return 0;
    }
    //接收数据
    Ptri = 0;
    DWORD dwSize;
    LPSTR pszOutBuffer;
    DWORD dwDownloaded = 0;
    do {
        // 检查可用数据
        dwSize = 0;
        if (!WinHttpQueryDataAvailable(hRequest, &dwSize)) {
            XCL_log(L"Error ", GetLastError(), L" in WinHttpQueryDataAvailable.\n");
            break;
        }
        // 没有更多的可用数据
        if (!dwSize) {
            break;
        }
        //为缓冲区分配空间
        pszOutBuffer = new char[dwSize + 1];
        if (!pszOutBuffer) {
            XCL_log(L"Out of memory\n");
            break;
        }
        // 读入数据开始.
        ZeroMemory(pszOutBuffer, dwSize + 1);
        if (!WinHttpReadData(hRequest, (LPVOID)pszOutBuffer, dwSize,
            &dwDownloaded)) {
            XCL_log(L"Error ", GetLastError(), L" in WinHttpReadData.\n");
        }
        else {
            req += pszOutBuffer;
        }
        // 释放分配给缓冲区的内存空间.
        delete[] pszOutBuffer;
        // This condition should never be reached since WinHttpQueryDataAvailable
        // reported that there are bits to read.
        if (!dwDownloaded) {
            break;
        }
    } while (dwSize > 0);
    return req.size();
}

DWORD X_WinHttp::GetResponseBodyText(CXText& req) {
    CXTextA txt;
    GetResponseBodyTextA(txt);
    req = A2W_(txt.get());
    return req.size();
}

DWORD X_WinHttp::GetResponseBodyTextUtf8ToAnsi(CXText& req) {
    CXTextA txt;
    GetResponseBodyTextAUtf8ToAnsi(txt);
    req = A2W_(txt.get());
    return req.size();
}

DWORD X_WinHttp::GetResponseBodyTextA(CXTextA& req) {
    if (hRequest == 0 ) {
        return {};
    }
    DWORD Ptri;
    if (ResponseBodylen != NULL) {
        req = "";
        return 0;
    }
    //接收数据
    Ptri = 0;
    DWORD dwSize;
    LPSTR pszOutBuffer;
    DWORD dwDownloaded = 0;
    do {
        // 检查可用数据
        dwSize = 0;
        if (!WinHttpQueryDataAvailable(hRequest, &dwSize)) {
            XCL_log(L"Error ", GetLastError(), L" in WinHttpQueryDataAvailable.\n");
            break;
        }
        // 没有更多的可用数据
        if (!dwSize) {
            break;
        }
        //为缓冲区分配空间
        pszOutBuffer = new char[dwSize + 1];
        if (!pszOutBuffer) {
            XCL_log(L"Out of memory\n");
            break;
        }
        // 读入数据开始.
        ZeroMemory(pszOutBuffer, dwSize + 1);
        if (!WinHttpReadData(hRequest, (LPVOID)pszOutBuffer, dwSize,
            &dwDownloaded)) {
            XCL_log(L"Error ", GetLastError(), L" in WinHttpReadData.\n");
        }
        else {
            req += pszOutBuffer;
        }
        // 释放分配给缓冲区的内存空间.
        delete[] pszOutBuffer;
        // This condition should never be reached since WinHttpQueryDataAvailable
        // reported that there are bits to read.
        if (!dwDownloaded) {
            break;
        }
    } while (dwSize > 0);
    return req.size();
}

DWORD X_WinHttp::GetResponseBodyTextAUtf8ToAnsi(CXTextA& req) {
    GetResponseBodyTextA(req);
    req = W_Coding_Utf82Gbk(req);
    return req.size();
}

int X_WinHttp::GetStatus() { return _wtoi(GetStatusText()); }

CXText X_WinHttp::GetStatusText() {
    if (!hRequest) {
        return L"0";
    }
    DWORD len;
    WinHttpQueryHeaders(hRequest, WINHTTP_QUERY_STATUS_CODE, NULL, NULL, &len,
        NULL);
    wchar_t* bin = new wchar_t[len];
    if (WinHttpQueryHeaders(hRequest, WINHTTP_QUERY_STATUS_CODE, NULL, bin, &len,
        NULL) <= 0) {
        delete bin;
        return 0;
    }
    CXText r = bin;
    delete bin;
    return r;
}

CXText X_WinHttp::GetAllResponseHeaders() { return ResponseHeaders; }

CXText X_WinHttp::GetResponseHeader(CXText Header) {
    if (ResponseHeaders == L"") {
        return L"";
    }
    CXText str = L"\n";
    str += Header + ":";
    CXText ret;
    DWORD i = ResponseHeaders.find(str);
    if (i != std::wstring::npos) {
        i += str.size();
        DWORD ii = ResponseHeaders.find2(L"\n", i);
        if (i != std::wstring::npos) {
            ret = ResponseHeaders.substr(i, ii - i);
            lTrim(ret);
        }
    }
    return ret;
}

CXText X_WinHttp::GetAllResponseCookies() {
    if (ResponseHeaders == L"") {
        return L"";
    }
    CXText cookie, Cookies;
    int i = ResponseHeaders.find(L"Set-Cookie:");
    int ii = 0;
    while (i != std::wstring::npos && ii != std::wstring::npos) {
        if (i > (int)ResponseHeaders.size()) {  // 64位情况下可能会出现溢出导致无限循环,所以手动跳出
            break;
        }
        ii = ResponseHeaders.find2(L";", i);
        if (ii != std::wstring::npos) {
            ii++;
            cookie = ResponseHeaders.substr(i, ii - i);
            lTrim(cookie);
            CXText Value = cookie.substr(cookie.size() - 9, cookie.size());
            bool del = Value == L"=deleted;" || Value == L"=DELETED;" ||
                Value == L"=Deleted;";  // 判断是否是删除标记
            if (!del) {
                Cookies = Cookies + cookie + L" ";
            }
            i = ResponseHeaders.find2(L"Set-Cookie:", ii);
        }
    }
    if (Cookies != L"") {
        Cookies = Cookies.substr(0, Cookies.size() - 2);
    }
    return Cookies;
}

CXText X_WinHttp::GetResponseCookie(CXText Name) {
    if (ResponseHeaders == L"") {
        return L"";
    }
    CXText str = L"Set-Cookie: " + Name + L"=", ret;
    DWORD i = ResponseHeaders.find(str);
    if (i != std::wstring::npos) {
        i += str.size();
        DWORD ii = ResponseHeaders.find2(L";", i);
        if (ii != std::wstring::npos) {
            ret = ResponseHeaders.substr(i, ii - i);
        }
        else {
            str = L"Set-Cookie: " + Name + "=";
            i = ResponseHeaders.find(str);
            if (i != std::wstring::npos) {
                i += str.size();
                ii = ResponseHeaders.find2(L";", i);
                if (ii != std::wstring::npos) {
                    ret = ResponseHeaders.substr(i, ii - i);
                }
            }
        }
    }
    return ret;
}

CXText X_WinHttp::GetResponseLocation(bool autobUrl) {
    CXText ret;
    if (ResponseHeaders == L"") {
        return L"";
    }
    DWORD i = ResponseHeaders.find(L"Location:");
    if (i != std::wstring::npos) {
        i += 9;
        DWORD ii = ResponseHeaders.find2(L"\n", i);
        if (ii != std::wstring::npos) {
            ret = ResponseHeaders.substr(i, ii - i);
            Trim(ret);
            if (ret != L"") {
                if (autobUrl) {
                    CXText str = ret;
                    w_tolower(str);
                    if (ret.substr(0, 1) == L"/") {  //比如 /demo/index.html
                        CXText l = Scheme == 2 ? L"https://" : L"http://";
                        l += HostName + ":";
                        wchar_t str[40];
                        _itow_s(Port, str, 40);
                        l += str;
                        ret = l + ret;
                    }
                    if (str.substr(0, 7) != L"http://" &&
                        str.substr(0, 8) != L"https://")  // 比如index2.html
                    {
                        i = rFind(UrlW, L"/");
                        if (i == std::wstring::npos) {
                            ret = UrlW + L"/" + ret;
                        }
                        else {
                            ret = UrlW.substr(0, i) + UrlW;
                        }
                    }
                }
            }
        }
    }
    return ret;
}

bool X_WinHttp::SetTimeouts(int ResolveTimeout, int ConnectTimeout,
    int SendTimeout, int ReceiveTimeout) {
    if (!hSession) {
        return false;
    }
    return WinHttpSetTimeouts(hSession, ResolveTimeout, ConnectTimeout,
        SendTimeout, ReceiveTimeout) > 0;
}

bool X_WinHttp::SetProxy(CXText Proxy, CXText User, CXText Pass) {
    if (!hSession) {
        return false;
    }
    WINHTTP_PROXY_INFO op;
    if (Proxy == L"") {
        op.dwAccessType = WINHTTP_ACCESS_TYPE_NO_PROXY;
        op.lpszProxy = NULL;
        op.lpszProxyBypass = NULL;
        return WinHttpSetOption(hSession, WINHTTP_OPTION_PROXY, &op, 12) > 0;
    }
    if (hRequest) {  //如果是在Open之后调用 可直接设置代理信息
        WinHttpSetOption(hRequest, WINHTTP_OPTION_PROXY_USERNAME, (void*)User.get(),
            User.size());
        WinHttpSetOption(hRequest, WINHTTP_OPTION_PROXY_PASSWORD, (void*)Pass.get(),
            Pass.size());
    }
    else {
        SetProxyInfo = true;
        SetProxyUser = User;
        SetProxyPass = Pass;
    }
    op.dwAccessType = WINHTTP_ACCESS_TYPE_NAMED_PROXY;
    op.lpszProxy = (LPWSTR)Proxy.get();
    op.lpszProxyBypass = NULL;
    return WinHttpSetOption(hSession, WINHTTP_OPTION_PROXY, &op,
        sizeof(WINHTTP_PROXY_INFO)) > 0;
}

bool X_WinHttp::SetCredentials(CXText UserName, CXText Password, int AuthTargets,
    int AuthScheme) {
    if (!hRequest) {
        return false;
    }
    return WinHttpSetCredentials(hRequest, AuthTargets, AuthScheme, UserName,
        Password, NULL) > 0;
}

bool X_WinHttp::SetOption(int Type, int Option, LPVOID ValueBuffer,
    int BufferLength) {
    HINTERNET hInternet = 0;
    switch (Type) {
    case 1:
        hInternet = hConnect;
        break;
    case 2:
        hInternet = hRequest;
    case 3:
        hInternet = hSession;
    default:
        hInternet = hConnect;
        break;
    }
    return WinHttpSetOption(hInternet, Option, ValueBuffer, BufferLength) > 0;
}

void X_WinHttp::SetRedirect(bool IsRedirect) { NeverRedirect = !IsRedirect; }

bool X_WinHttp::SetRequestHeader(CXText Header, CXText Value, int Modifiers) {
    if (!hRequest || Header == L"") {
        return false;
    }
    CXText t = Header;
    w_tolower(t);
    if (_AutoCookies && t == L"cookie") {
        DWORD cset = WINHTTP_DISABLE_COOKIES;
        WinHttpSetOption(hRequest, WINHTTP_OPTION_DISABLE_FEATURE, &cset,
            sizeof(cset));  //设置禁止Winhttp内部自动处理Cookies
    }
    CXText HeaderW = Header + L":" + Value;
    return WinHttpAddRequestHeaders(hRequest, HeaderW, HeaderW.size(),
        Modifiers) > 0;
}

void X_WinHttp::SetRequestHeaders(CXText Headers) {
    auto list = X_String_SplitWithStl(Headers,L"\n");
    for (int i = 0; i < (int)list.size();i++) {
        size_t index = list[i].find(L":");
        if (index != -1) {
            SetRequestHeader(list[i].substr(0, index), list[i].substr(index, list[i].size()- index));
        }
	}
}

bool X_WinHttp::DelRequestHeader(CXText Header) {
    if (!hRequest || Header == L"") {
        return false;
    }
    CXText t = Header;
    w_tolower(t);
    if (_AutoCookies && t == L"cookie") {
        DWORD cset = WINHTTP_DISABLE_COOKIES;
        WinHttpSetOption(hRequest, WINHTTP_OPTION_DISABLE_FEATURE, &cset,
            sizeof(cset));  //设置禁止Winhttp内部自动处理Cookies
    }
    CXText HeaderW = Header + L":";
    return WinHttpAddRequestHeaders(hRequest, HeaderW, HeaderW.size(),
        WINHTTP_ADDREQ_FLAG_REPLACE) > 0;
}

bool X_WinHttp::SetCookie(CXText Cookie) {
    if (_AutoCookies == 0) {
        if (!hRequest) {
            return false;
        }
        DWORD cset = WINHTTP_DISABLE_COOKIES;
        WinHttpSetOption(hRequest, WINHTTP_OPTION_DISABLE_FEATURE, &cset,
            sizeof(cset));  //设置禁止Winhttp内部自动处理Cookies
        return SetRequestHeader(L"Cookie", Cookie);
    }
    else if (_AutoCookies == 1) {
        return CookieAdd(Cookie);
    }
    else if (_AutoCookies == 2) {
        return __SetCookies2(Cookie);  //处理更新到IES缓存
    }
    return false;
}

bool X_WinHttp::SetUserAgent(CXText UserAgentValue) {
    return SetRequestHeader(L"User-Agent", UserAgentValue);
}

bool X_WinHttp::SetReferer(CXText RefererValue) {
    if (RefererValue == L"") {
        RefererValue = UrlW;
    }
    return SetRequestHeader(L"Referer", RefererValue);
}

bool X_WinHttp::SetAccept(CXText AcceptValue) {
    return SetRequestHeader(L"Accept", AcceptValue);
}

bool X_WinHttp::SetAcceptLanguage(CXText AcceptLanguageValue) {
    return SetRequestHeader(L"Accept-Language", AcceptLanguageValue);
}

bool X_WinHttp::SetContentType(CXText ContentTypeValue) {
    return SetRequestHeader(L"Content-Type", ContentTypeValue);
}

bool X_WinHttp::SetXMLHttpRequest() {
    return SetRequestHeader(L"x-requested-with", L"XMLHttpRequest");
}

bool X_WinHttp::SetFakeIP(CXText FakeIP) {
    if (!hRequest) {
        return false;
    }
    CXText Header = L"X-Forwarded-For: " + FakeIP + L"\n";
    Header += L"CLIENT_IP: " + FakeIP + L"\n";
    Header += L"VIA: " + FakeIP + L"\n";
    Header += L"REMOTE_ADDR: " + FakeIP;
    return WinHttpAddRequestHeaders(
        hRequest, Header, Header.size(),
        WINHTTP_ADDREQ_FLAG_ADD | WINHTTP_ADDREQ_FLAG_REPLACE) > 0;
}

CXText X_WinHttp::Cookies(CXText NewCookies) {
    XCL_log(L"1");
    if (NewCookies == L"") {
        return _Cookies.size() < 7 ? _Cookies : _Cookies.substr(2, _Cookieslen - 3);
    }
    XCL_log(L"1");
    CXText str;
    if (NewCookies.substr(0, 2) != L"; ") {
        str = L"; " + NewCookies;
    }
    else {
        str = NewCookies;
    }
    XCL_log(L"2");
    if (str.substr(str.size() - 2, 2) != L"; ") {
        CXText text = str.substr(str.size() - 1, 1);
        if (text != L";") {
            str += L";";
        }
        if (text != L" ") {
            str += L" ";
        }
    }
    XCL_log(L"3");
    _Cookies = str;
    _Cookieslen = _Cookies.size();
    return NewCookies;
}

CXText X_WinHttp::CookieGet(CXText Name, bool AddName) {
    CXText str;
    if (Name == L"") {
        return str;
    }
    // Cookie名 区分大小写
    UINT i = _Cookies.find(L"; " + Name);
    if (i != std::wstring::npos) {
        i += 2;
        int len = Name.size();
        UINT ii = _Cookies.find2(L";", i + len);
        if (ii != std::wstring::npos) {
            if (!AddName) {
                i = i + len + 1;
            }
            str = _Cookies.substr(i, ii - i);
        }
    }
    return str;
}

bool X_WinHttp::CookieAdd(CXText Cookie) {
    if (Cookie == L"") {
        return false;
    }
    std::vector<CXText> strs = split(Cookie, L';');
    DWORD i = 0;
    bool vbool = false;
    for (size_t jc = 0; jc < strs.size(); jc++) {
        lTrim(strs[jc]);
        i = strs[jc].find(L"=");
        if (i != std::wstring::npos) {
            CXText name = L"; " + strs[jc].substr(0, i - 1) + L"=";
            i = _Cookies.find(name);
            if (i != std::wstring::npos) {
                DWORD ii = _Cookies.find2(L"; ", i + name.size());
                if (ii != std::wstring::npos) {
                    i += 2;
                    _Cookies.replace(i, ii - i, strs[jc]);
                }
            }
            else {
                _Cookies += strs[jc] + L"; ";
                vbool = true;
            }
        }
    }
    if (vbool) {
        _Cookieslen = _Cookies.size();
    }
    return vbool;
}

bool X_WinHttp::CookieDel(CXText Cookie) {
    if (Cookie == L"") {
        return false;
    }
    bool vbool = false;
    std::vector<CXText> strs = split(Cookie, L';');
    for (size_t jc = 0; jc < strs.size(); jc++) {
        lTrim(strs[jc]);
        UINT i = strs[jc].find(L"=");
        if (i != std::wstring::npos) {
            strs[jc] = strs[jc].substr(0, i);
        }
        i = _Cookies.find(strs[jc]);
        if (i != std::wstring::npos) {
            UINT ii = _Cookies.find2(L"; ", i + strs[jc].size());
            if (ii != std::wstring::npos) {
                int n = ii - i;
                std::wstring t(_Cookies.get());
                t.replace(t.begin() + i, t.begin() + ii + 1, L"");
                _Cookies = t.c_str();
                //我超？？？迭代器报错？？日了g了c
                //_Cookies.replace(i, ii, L"");

                vbool = true;
            }
        }
    }
    if (vbool) {
        _Cookieslen = _Cookies.size();
    }
    return vbool;
}
